package cn.aiweiyi.qingjing.coupon.template.service.impl;

import cn.aiweiyi.qingjing.coupon.comm.constant.Constant;
import cn.aiweiyi.qingjing.coupon.template.dao.CouponTemplateDao;
import cn.aiweiyi.qingjing.coupon.template.entity.CouponTemplate;
import cn.aiweiyi.qingjing.coupon.template.service.AsyncService;
import com.google.common.base.Stopwatch;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author 青衿
 * @Date 2020/11/17 11:24
 * 异步服务实现类,负责由线程池调用执行产生优惠码,存入redis
 * 并生成成功后,更新数据库中模板的状态为可用
 **/
@Slf4j
@Service
public class AsyncServiceImpl implements AsyncService {
    @Autowired
    CouponTemplateDao couponTemplateDao;

    @Autowired
    StringRedisTemplate redisTemplate;

    @Async("getAsyncExecutor")
    @Override
    public void asyncConstructCouponByTemplate(CouponTemplate template) {
        Stopwatch stopwatch = Stopwatch.createStarted();
        Set<String> couponCodes = buildCouponCode(template);
        //ketai_coupon_template_code_1
        String redisKey = Constant.RedisPrefix
                .COUPON_TEMPLATE + template.getId();
        log.info("为模板{}生成一批优惠券码,存入Redis{}",
                redisKey,
                redisTemplate.opsForList().rightPushAll(redisKey,
                        couponCodes));
        template.setAvailable(true);
        couponTemplateDao.save(template);
        stopwatch.stop();
        log.info("创建模板并产生优惠券码成功,耗时{}ms",
                stopwatch.elapsed(TimeUnit.MILLISECONDS));
        //发送短信或者邮件通知优惠券已经可用
        log.info("模板({})已经可用!", template.getId());
    }

    //根据模板生成一批优惠券码,存入Set并返回
    private Set<String> buildCouponCode(CouponTemplate template) {
        Stopwatch watch = Stopwatch.createStarted();
        Set<String> codes = new HashSet<>(template.getCount());
        String prefix = template.getProductLine().getCode()
                .toString() + template.getCategory().getCode();
        String date = new SimpleDateFormat("yyMMdd")
                .format(template.getCreateTime());
        //先按模板的数量要求,用for循环生成一批优惠券码
        for (int i = 0; i < template.getCount(); i++) {
            codes.add(prefix + buildCouponCodeSuffix14(date));
        }
        //可能有重复的,Set会去掉重复的,所以数量可能不够,在补充一轮
        while (codes.size() < template.getCount()) {
            codes.add(prefix + buildCouponCodeSuffix14(date));
        }
        //断言生成的数量与模板要求的数量一致
        assert codes.size() == template.getCount();
        watch.stop();
        log.info("生成优惠券码成功,耗时{}ms",
                watch.elapsed(TimeUnit.MILLISECONDS));
        return codes;
    }

    //产生不重复的优惠券码后14个字符,前6位来自于日期,后八位来自于随机数
    private String buildCouponCodeSuffix14(String date) {
        char[] bases = new char[]{'1', '2', '3', '4', '5', '6', '7', '8', '9'};
        //中间六位
        List<Character> chars = date.chars().mapToObj(
                e -> (char) e).collect(Collectors.toList());
        Collections.shuffle(chars);
        String mid6 = chars.stream().map(Object::toString)
                .collect(Collectors.joining());

        //后八位
        String suffix8 = RandomStringUtils.random(1, bases)
                + RandomStringUtils.randomNumeric(7);
        return mid6 + suffix8;
    }


    //产生不重复的优惠券码后14个字符,前6位来自于日期,后八位来自于随机数
//    private String buildCouponCodeSuffix14(String date){
//        char[] bases=new char[]{'1','2','3','4','5','6','7','8','9'};
//        List<Character> characterList = date.chars().mapToObj(
//                e ->(char)e).collect(Collectors.toList());
//        Collections.shuffle(characterList);
//        String mid6=characterList.stream().map(Objects::toString).collect(Collectors.joining());
//        String suffix8= RandomStringUtils.random(1,bases)
//                +RandomStringUtils.randomNumeric(7);
//        return mid6 + suffix8;
//    }
}
